/*
Copyright (c) [2019年5月1日] [吴超]
[MBT_Studio] is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
		 http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
*/
#pragma once

#include "UI/UI.h"
#include "面/面.h"


#include <节点/节点.h>
#include <节点/节点树.h>
#include "ui属性栏.h"



typedef enum {
	e_节点区_节点视图,
	e_节点区_数值,
	e_节点区_图像,
	e_节点区_物体,
	e_节点区_材质,
	e_节点区_曲线,
	e_节点区_关键帧,
}E_节点区类型;


typedef struct {
	class C_ui节点插座* 头;
	class C_ui节点插座* 尾;

	class C_节点视口* 发起的节点树;
	class C_节点面板* 发起的节点;
}S_插座拖放数据;




typedef void(*fp_节点面板绘制Ex)(class C_节点面板* node);
typedef void(*fp_节点面板3D视口)(class C_节点面板* node, S_Scene* scene);
typedef C_节点面板* (*fp_从节点构建节点回调)(C_节点基类* node, bool 是否关联, bool 创建新节点);
typedef C_节点面板* (*fp_节点构建_重构)(C_节点面板* plane, C_节点基类* node);
typedef void(*fp_节点创建视口回调)(C_Widget* node, bool 是否清除以前激活节点);
typedef void(*fp_节点添加到视口回调)(std::vector<class C_节点面板*>& node, bool 是否清除以前激活节点);
typedef C_节点面板*(*FP_用名称构建节点面板)(const std::string& 节点类型名称);

typedef C_节点面板* (*f_节点面板构建_回调函数指针)(S_UI创建环境& ctx, S_设备环境& ctx_3d, C_节点基类* node, C_节点树* plane);
typedef void		(*f_节点面板重置_回调函数指针)(C_节点面板* plane, C_节点基类* node);

//typedef C_节点面板* (*fp_节点构建回调)(S_UI创建环境& ctx, S_设备环境& view_ctx, C_节点基类* node, C_节点面板* plane);


class C_ui节点插座 : public C_Widget {
	
public:
	C_ui节点插座(class C_节点面板* node, C_插座基类* socket);
	~C_ui节点插座();

	C_插座基类*			m_SocketData;
	//void*		m_连接线;
	class C_节点面板*	m_NodePlane;
	//void*				m_连接线_In口;
	//std::vector<void*>	m_连接线_Out口;


	static vec2 m_起点;
	static vec2 m_终点;
	//struct S_NodeLink* m_Link;
	
};




class C_节点面板 : public C_Widget {
	//S_Surface2D* m_Surface;

protected:
	std::vector<C_ui节点插座*> m_InSocket;
	std::vector<C_ui节点插座*> m_OutSocket;

	
public:
	C_节点面板(C_节点基类* nd, S_UI创建环境& ctx, E_节点区类型 区类型 = E_节点区类型::e_节点区_物体);
	~C_节点面板();

	C_节点基类* m_NodeData;
	C_Widget*	m_节点当前所在视口;


	C_Widget*	         m_内部面板;
	C_Widget*	         m_拖拽到其他视口按钮;
	C_Widget*	         m_独显按钮;
	std::set<C_Widget*>* m_同子集节点树关联UI部件;

	
	vec2		m_拾取区域大小;
	float32     m_节点面板宽度;
	float32		m_高比例;
	S_RGBA8UI   m_焦点颜色;
	std::string m_ICO;

	
	static float32	m_链接点直径;
	E_方向			m_绘制排列方式;
	E_节点区类型	m_区;
	uint8			m_更新时间;
	bool			m_是否已弃用;


	fp_节点面板绘制Ex		m_扩展绘制;
	fp_节点面板3D视口		m_3D视口更新;
	fp_属性栏面板_部件构建	m_属性面板参数构建;


	uint32					m_背景GPU内存偏移;
	uint32					m_文字GPU内存偏移;
	uint32					m_链接线GPU内存偏移;
	
	static std::map<std::string, f_节点面板构建_回调函数指针> g节点面板构建函数;
	static std::map<std::string, f_节点面板重置_回调函数指针> g节点面板重置函数;
	static bool    m_移动面板;




	void f_addInSocket(C_插座基类* socketData, int16 offset = -1);
	void f_addOutSocket(C_插座基类* socketData, int16 offset = -1);

	void f_removeInSocket(int16 offset = -1);
	void f_removeOutSocket(int16 offset = -1);

	void f_releaseAllSocket();


	C_ui节点插座* f_getInSocket(uint16 id);
	C_ui节点插座* f_getOutSocket(uint16 id);
	

	ivec4	f_get插座GPU参数();
	void	f_插座横向变换更新();
	void	f_插座纵向排列布局更新();
	void	f_从后端重建插座();


	const std::vector<C_ui节点插座*>& f_getInSocket()	{ return m_InSocket; }
	const std::vector<C_ui节点插座*>& f_getOutSocket()	{ return m_OutSocket; }
	
	//参数 offset  从1开始
	S_Line f_计算横向排列插座连接线坐标(S_NodeLink* link, uint16 接入数量, uint16 offset, uint8 插座类型);
	S_Line f_计算纵向排列插座连接线坐标(S_NodeLink* link, uint16 接入数量, uint16 offset, uint8 插座类型);

	//void f_插座绑定连接线(std::map<std::string, C_节点面板*>& tempPlaneMap);
	void f_动态添加插座(C_插座基类* 输入插座);
	void f_动态删除插座();
	std::vector<C_节点面板*> f_切断链接(vec2* lines, uint32 num);
	std::vector<C_节点面板*> f_切断园公切线链接(vec2* lines, uint32 num, float32 最大半径);
	void f_设置焦点(bool 是否开启焦点绘制);
	bool f_是否有焦点() { return m_NodeData->m_是否有焦点; }

	//void f_设置面板大小(float32 最小宽度);

};






//void f_nodeplaen_添加关键帧焦点面板(C_节点面板* plane);
//void f_nodeplaen_添加曲线焦点面板(C_节点面板* plane);
//void f_nodeplane_添加图像焦点面板(C_节点面板* plane);




class C_节点操作_移动节点 : public C_操作记录 {

public:
	C_节点操作_移动节点(std::vector<C_节点基类*>& nodes, const vec2& 鼠标位置);

	static C_节点操作_移动节点* g当前移动操作;

	struct S_节点移动记录 {
		C_节点基类* m_node;
		vec2		m_移动前坐标;
		vec2		m_移动后坐标;
		vec2		m_相对起始坐标;
	};
	std::vector<S_节点移动记录>	m_Nodes;

	//std::vector<vec2>			m_记录初始坐标;
	//std::vector<vec2>			m_连带节点记录初始坐标;
	//std::map<C_节点基类*, vec2> m_连带节点记录初始坐标;

	void f_撤销();
	void f_重做();

	void f_移动(const vec2& 标准坐标);
protected:
	void f_记录(std::vector<C_节点基类*> nodes, const vec2& 起始中心);
	
};







struct S_链接线记录 {
	C_节点基类* m_OutNode;
	C_节点基类* m_InNode;
	
	S_NodeLink*	m_链接线;
	uvec2		m_链接ID;
};

class C_节点操作_切断链接 : public C_操作记录 {
	//std::vector<S_NodeLink*> m_断开Link;
	//std::vector<uvec2> m_链接线链接插座ID;
	std::vector<S_链接线记录> m_链接线链接插座ID;
	S_NodeLink* m_同时Link;

public:
	C_节点操作_切断链接(std::vector<S_链接线记录>& links, S_NodeLink* link) : m_链接线链接插座ID(links), m_同时Link(link) {
	}
	~C_节点操作_切断链接() {
		for (auto& e : m_链接线链接插座ID) {
			//f_NodeLine_回收(e);
			//f_NodeLine_回收(m_同时Link);
		}
	}

	void f_撤销() {
		if (m_同时Link) {
			m_同时Link->f_断开输出链接();
			m_同时Link->m_插座out->m_UI部件->m_父部件->m_绘制属性更新 = true;
		}
		//for (auto& e : m_断开Link) {
		//	e->f_Link(e->m_插座in, e->m_插座out);
		//	e->m_插座out->m_UI部件->m_父部件->m_绘制属性更新 = true;
		//}
		for (auto& e : m_链接线链接插座ID) {
			//e.m_链接线->f_Link(e.m_InNode->f_get输出插座(e.m_链接ID.x), e.m_OutNode->f_get输入插座(e.m_链接ID.y));
			//e.m_链接线->f_Link(e.m_InNode->f_get输入插座(e.m_链接ID.x), e.m_OutNode->f_get输出插座(e.m_链接ID.y));
			e.m_链接线->f_Link(e.m_OutNode->f_get输入插座(e.m_链接ID.x), e.m_InNode->f_get输出插座(e.m_链接ID.y));
			for (auto& ui : e.m_InNode->m_包含在多个视口的UI部件) {
				ui->m_绘制属性更新 = true;
			}
		}
	}

	void f_重做() {
		for (auto& e : m_链接线链接插座ID) {
			//e.m_InNode->f_get输入插座(e.m_链接ID.x);
			//e.m_OutNode->f_get输入插座(e.m_链接ID.y);

			e.m_链接线->f_断开输出链接();
			e.m_链接线->m_插座out->m_UI部件->m_父部件->m_绘制属性更新 = true;
		}

		if (m_同时Link) {
			m_同时Link->f_Link(m_同时Link->m_插座in, m_同时Link->m_插座out);
			m_同时Link->m_插座out->m_UI部件->m_父部件->m_绘制属性更新 = true;
		}
	}
};

class C_节点操作_连接链接 : public C_操作记录 {
	S_NodeLink* m_Link;
public:
	C_节点操作_连接链接(S_NodeLink* link) : m_Link(link) {
	}
	~C_节点操作_连接链接() {
		f_NodeLine_回收(m_Link);
	}

	//vector<uint32> m_全局中的线ID;
	void f_撤销() {
		m_Link->f_断开输出链接();
		m_Link->m_插座out->m_UI部件->m_父部件->m_绘制属性更新 = true;
	}
	void f_重做() {
		m_Link->f_Link(m_Link->m_插座in, m_Link->m_插座out);
		m_Link->m_插座out->m_UI部件->m_父部件->m_绘制属性更新 = true;
	}
};



void f_NodePlane_数值更新刷新节点(C_Widget* self);

void f_NodePlane_更新节点内部面板(C_Widget* self);
void f_NodePlane_更新节点内部面板布局(C_节点面板* 节点面板, E_方向 方向);
S_Bounding2D f_NodePlane_计算包含节点边界框(C_节点面板* 节点面板);



void f_NodePlane_鼠标设置节点坐标(C_节点面板* 节点面板, vec2 鼠标位置);
void f_NodePlane_设置后端节点坐标(C_节点面板* 节点面板, vec2 原始坐标);
void f_NodePlane_更新布局(C_节点面板* 节点面板);
void f_NodePlane_更新横向排列节点坐标(C_节点面板* 节点面板);

void f_NodePlane_链接节点(C_节点面板* out节点, uint16 outSocketInID, C_节点面板* in节点, uint16 inSocketInID);

void f_NodePlane_切换插座类型(C_节点面板* 节点面板, E_插座方向 方向, uint16 loc, E_值类型 类型);


Inline vec2 f_NodePanel_get全局坐标(C_节点基类* 节点, S_View属性* 视口属性) {
	return 节点->m_坐标 * 视口属性->m_比例 + 视口属性->m_中心;
}
Inline vec2 f_NodePanel_get标准坐标(const vec2& 组件内坐标, S_View属性* 视口属性) {
	return (组件内坐标 - 视口属性->m_中心) / 视口属性->m_比例;
}
Inline vec2 f_NodePanel_get标准坐标(const vec2& 组件内坐标, C_Widget* 组件) {
	return (组件内坐标 - 组件->m_扩展视口属性->m_中心) / 组件->m_扩展视口属性->m_比例;
}

vec2 f_NodePlane_get中心坐标(C_节点面板* 节点面板);
vec2 f_NodePlane_get中心偏移(C_节点面板* 节点面板);
Inline vec2 f_NodePlane_get拾取区域大小(C_节点面板* 节点面板) {
	节点面板->m_内部面板->f_getOriginalSize();
}

void f_NodePanel_get边界框(const C_节点面板* 节点面板, mat2x4& point);
void f_NodePanel_更新框(C_节点面板* 节点面板);
bool f_NodePanel_是否已在框内(const C_节点基类* 要添加到框的节点, const C_节点基类* 要对照框节点);


Inline void f_NodePanel_get边界框(const C_节点面板* 节点面板, S_Bounding2D& box) {
	box.min = 节点面板->f_getOriginalPos();
	box.max = box.min + 节点面板->f_getOriginalSize();
}


E_事件是否传递 f_NodePlane_进入子集(C_Widget* self, uint8 状态);





